#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>

typedef long long ll;
const int N = 200005, M = 50005;
int n, m, cnt, cntq, cntq2;
int x[M], y[M], opt[M];
ll ans[M];

struct Query {
  int id, t, x;
  inline bool operator<(const Query &rhs) const { return t < rhs.t; }
} q[10000005];
std::vector<Query> G[M];

struct Query2 {
  int id, t, l, r;
  inline bool operator<(const Query2 &rhs) const { return t < rhs.t; }
} q2[10000005];

struct DivBlock {
  int n, m, blo;
  int tag[N], a[N], No[N], l[N], r[N];

  inline void init() {
    blo = ceil(sqrt(n));
    for (int i = 1; i <= n; ++i) No[i] = (i - 1) / blo + 1;
    m = No[n];
    for (int i = 1; i <= m; ++i) {
      l[i] = (i - 1) * blo + 1, r[i] = std::min(n, i * blo), tag[i] = -1;
    }
  }

  inline void rebuild(int id) {
    if (tag[id] != -1)
      for (int i = l[id]; i <= r[id]; ++i) a[i] = tag[id];
    tag[id] = -1;
  }

  inline void add(int L, int R, int v) {
    static int id;
    if (No[L] == No[R]) {
      id = No[L];
      rebuild(id);
      for (int i = L; i <= R; ++i) a[i] = v;
      return;
    }
    rebuild(id = No[L]);
    for (int i = L; i <= r[id]; ++i) a[i] = v;
    rebuild(id = No[R]);
    for (int i = l[id]; i <= R; ++i) a[i] = v;
    for (int i = No[L] + 1; i < id; ++i) tag[i] = v;
  }

  inline int ask(int x) { return tag[No[x]] == -1 ? a[x] : tag[No[x]]; }
} mdiv;

struct UnionFindSet {
  int fa[N];
  ll tag[N], val[N];
  std::vector<int> g[N];

  inline void init() {
    for (int i = 1; i <= n; ++i) {
      fa[i] = i, tag[i] = val[i] = 0;
      g[i].clear(), g[i].push_back(i);
    }
  }

  int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

  inline void merge(int x, int y) {
    x = find(x), y = find(y);
    if (x == y) return;
    if (g[x].size() < g[y].size()) std::swap(x, y);
    for (std::vector<int>::iterator i = g[y].begin(); i != g[y].end(); ++i)
      val[*i] += tag[y];
    tag[y] = 0;
    fa[y] = x;
    for (std::vector<int>::iterator i = g[y].begin(); i != g[y].end(); ++i)
      val[*i] -= tag[x], g[x].push_back(*i);
    g[y].clear();
  }

  inline ll query(int x) { return val[x] + tag[find(x)]; }

  inline void add(int x, int v) { tag[find(x)] += v; }
} ufs;

int main() {
#ifndef ONLINE_JUDGE
#ifdef LOCAL
  freopen("testdata.in", "r", stdin);
  freopen("testdata.out", "w", stdout);
#endif
#ifndef LOCAL
  freopen("淘淘的集合.in", "r", stdin);
  freopen("淘淘的集合.out", "w", stdout);
#endif
#endif
  scanf("%d%d", &n, &m);
  mdiv.init();
  for (int i = 1; i <= m; ++i) {
    scanf("%d%d%d", &opt[i], &x[i], &y[i]);
    switch (opt[i]) {
      case 3:
        mdiv.add(x[i], y[i], i);
        break;
      case 4:
        q2[++cntq2].id = ++cnt, q2[cntq2].t = i, q2[cntq2].l = x[i],
        q2[cntq2].r = y[i];
        for (int j = x[i]; j <= y[i]; ++j) {
          if (mdiv.ask(j)) {
            q[++cntq].id = cnt, q[cntq].t = mdiv.ask(j), q[cntq].x = j;
            G[q[cntq].t].push_back(q[cntq]);
          }
        }
    }
  }
  std::sort(q2 + 1, q2 + cntq2 + 1);
  ufs.init();
  for (int i = 1, j = 1; i <= m; ++i) {
    switch (opt[i]) {
      case 1:
        ufs.merge(x[i], y[i]);
        break;
      case 2:
        ufs.add(x[i], y[i]);
        break;
    }
    for (std::vector<Query>::iterator j = G[i].begin(); j != G[i].end(); ++j) {
      // printf("ans: %d\n", j->x);
      ans[j->id] -= ufs.query(j->x);
    }
    for (; j <= cntq2 && q2[j].t <= i; ++j)
      for (int k = q2[j].l; k <= q2[j].r; ++k) ans[q2[j].id] += ufs.query(k);
  }
  for (int i = 1; i <= cnt; ++i) printf("%lld\n", ans[i]);
  return 0;
}